#include <IoData.h>
#include <GeoSource.h>
#include <Domain.h>
#include <Modal.h>
#include <NonlinearRomOffline.h>

#include <cstdio>
#include <cstdlib>
#include <csignal>

#ifdef DO_MODAL
#include <ArpackUtil.h>
#endif

#include <fenv.h>
#include <Timer.h>

#include "OneDimensionalSolver.h"
#include "DebugTools.h"
#include "KirchhoffIntegrator.h"

extern void startNavierStokesSolver(IoData &, GeoSource &, Domain &);
extern void startModalSolver(Communicator *, IoData &, Domain &);
extern void startNonlinearRomOfflineSolver(Communicator *, IoData &, Domain &, GeoSource &);
extern void startSparseGridGeneration(IoData &, Domain &);
int interruptCode = 0;

//int  atexit(void (*function)(void)) { exit(-1);}

//------------------------------------------------------------------------------

extern "C" void processSignal(int num)
{

  if (num == SIGUSR1) {
    fprintf(stderr, "*** Warning: interruption generated by the user\n");
    interruptCode = 1;
  }

}

void segfault_sigaction(int signal, siginfo_t *si, void *arg)
{
    int rank;
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);
    printf("Caught segfault at address %p on MPI rank %d\n", si->si_addr,rank);
    MPI_Barrier(MPI_COMM_WORLD);
    exit(-1);
}

void fpe_sigaction(int signal, siginfo_t *si, void *arg)
{
    int rank;
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);
    printf("Caught floating point exception at address %p on MPI rank %d\n", si->si_addr,rank);
    MPI_Barrier(MPI_COMM_WORLD);
    exit(-1);
}

void abort_sigaction(int signal, siginfo_t *si, void *arg)
{
    int rank;
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);
    printf("Caught abort signal at address %p on MPI rank %d; likely due to an assertion failure\n", si->si_addr,rank);
    MPI_Barrier(MPI_COMM_WORLD);
    exit(-1);
}


//------------------------------------------------------------------------------

#ifdef CREATE_DSO
extern "C" int entrypoint(int argc, char **argv)
#else
int main(int argc, char **argv)
#endif
{
  // Navy requested that AERO-F look for a specific file if no input file is provided
  // at the command line 
  int argc0 = argc>1 ? argc : 2;
  char **argv0 = argc>1 ? argv : NULL;
  if(!argv0) {
    argv0 = new char*[2];
    argv0[0] = new char[512];
    argv0[1] = new char[16];
    std::strcpy(argv0[0],argv[0]);
    std::strcpy(argv0[1],"input.st");
  }

#ifndef CREATE_DSO
  initCommunication(argc0, argv0);
#endif

  //sleep(20);
  signal(SIGUSR1, processSignal);

#if !defined(__APPLE__) || !defined(__MACH__)
  feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW );
#endif

#ifdef AEROF_MPI_DEBUG

  bool debug_process = DebugTools::TryWaitForDebug();
  if (!debug_process) {
    struct sigaction sa;

    memset(&sa, 0, sizeof(struct sigaction));
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = segfault_sigaction;
    sa.sa_flags   = SA_SIGINFO;

    sigaction(SIGSEGV, &sa, NULL);
    
    sa.sa_sigaction = fpe_sigaction;
    sigaction(SIGFPE,&sa, NULL);
    
    sa.sa_sigaction = abort_sigaction;
    sigaction(SIGABRT,&sa, NULL);
  }
 
#endif


  Domain domain;
  Timer *timer = domain.getTimer();
//  fprintf(stderr,"TIMER::START TIME: %lf\n",timer->getTime());

  Communicator *com = domain.getCommunicator();
  // iodata obtains all the problem parameters from the cmd line and input file(s)
  IoData ioData(com);
  ioData.readCmdLine(argc0, argv0);
  ioData.readCmdFile();

  if(ioData.problem.alltype==ProblemData::_ONE_DIMENSIONAL_){

    double* mesh;
    int nPts;
    OneDimensional::load1DMesh(ioData,nPts,mesh);
    OneDimensional one(nPts, mesh, ioData,&domain);
    one.spatialSetup();
    one.stateInitialization(ioData.oneDimensionalInfo);
    one.totalTimeIntegration();
  }else if (ioData.problem.alltype==ProblemData::_SPARSEGRIDGEN_){
    startSparseGridGeneration(ioData,domain);
  }else{
    // obtain problem geometry
    GeoSource geoSource(ioData);
    geoSource.readConnectivityInfo(com);
    domain.getGeometry(geoSource, ioData);
    domain.numberEdges();
    domain.setNodeType(ioData);
    domain.setFaceToElementConnectivity();
    domain.makeRotationOwnership(ioData);
    domain.setInletNodes(ioData);
    domain.assignErrorHandler();

    domain.printElementStatistics();

    if (ioData.problem.alltype == ProblemData::_AERO_ACOUSTIC_)
    {
      if (com->cpuNum() == 0)
      {
        std::cout << "\n ... Aeroacoustic Postprocessing ... \n\n";
      }
      KirchhoffIntegrator doKP(ioData, &domain);
      doKP.Compute();
    } else if (ioData.problem.type[ProblemData::LINEARIZED]) {
      // Choose linearized fluid problem
      startModalSolver(com, ioData, domain);
    } else if (ioData.problem.type[ProblemData::NLROMOFFLINE]) {
      // Choose nonlinear ROM preprocessing solver
      startNonlinearRomOfflineSolver(com, ioData, domain, geoSource);
    } else {
      // Choose nonlinear fluid problem
      startNavierStokesSolver(ioData, geoSource, domain);
    }

  }

#ifndef CREATE_DSO
  closeCommunication();
#endif

  if(argc<=1) {
    delete [] argv0[0];
    delete [] argv0[1];
    delete [] argv0;
  }

  return 0;

}

//------------------------------------------------------------------------------
